package com.headwire.kw.helpers;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;



public class ExistingArticleRemover extends DefaultHandler
{
	// static state identifiers
	private static final int IN_VALUE = 1;
	
	private static final int SV_NODE =10;
	private static final int SV_PROPERTY = 11;
	private static final int SV_VALUE=12;
	
	private boolean inMap = false;
	private String curMapFrom = null;
	private ArrayList<String> curMapTo = null;
	
	private Node rootElem = null;
	private Node curNode = null;
	
	private int elemNum = 0;
	
	private HashMap<String,String> existingUrls = new HashMap<String,String>();
	
	class Node {
		ArrayList<Node> children = null;
		Attr[] attrs = null;
		int nIdx = 0;
		Node parent = null;
		int type;
		String text = "";
		public Node(Node p, int t) {
			parent = p;
			type = t;
			if(p != null) {
				p.addChild(this);
			}
		}
		public void addChild(Node c) {
			if(children == null)
				children=new ArrayList<Node>();
			children.add(c);
		}
		public void addAttr(String v, String t, String n) {
			if(attrs == null) {
				attrs = new Attr[2];
			}
			if(nIdx >= attrs.length) {
				Attr[] newAttrs = new Attr[attrs.length*2];
				for(int i = 0; i < attrs.length; i++) {
					newAttrs[i] = attrs[i];
				}
				attrs = newAttrs;
			}
			attrs[nIdx]=new Attr(v,t,n);
			nIdx++;
		}
	}
	
	class Attr {
		public Attr(String v,String t,String n) {
			value=v;
			type=t;
			name=n;
		}
		String value = "";
		String type = "";
		String name = "";
	}
	
	public ExistingArticleRemover(HashMap<String,String> urls) {
		if(urls != null)
			this.existingUrls = urls;
	}
	
	public void removeArticles() {
		curNode = rootElem;
		int nodesVisited = 0;
		while(curNode != null) {
			nodesVisited++;
			// check if this is a property node
			if(curNode.type == SV_PROPERTY) {
				// check for legacyURL
				if(isLegacyURL(curNode)) {
					Node val = curNode.children.get(0);
					// check if already imported
					if(existingUrls.containsKey(val.text)) {
						// delete this article!
						// property node's parent is jcr:content (sv:node)
						// So we want to remove curNode's grandfather.
						Node jcrContent = curNode.parent;
						Node article = jcrContent.parent;
						curNode = removeNode(article);
					}
					else {
						curNode = nextNode(curNode);
					}
				}
				else {
					// just move on
					curNode = nextNode(curNode);
				}
			}
			else {
				curNode = nextNode(curNode);
			}
		}
		System.out.println("Visited "+nodesVisited+" nodes.");
	}
	
	public void printXml(String outfile) throws IOException {
		XmlWriter xml = new XmlWriter(new FileWriter(outfile));
		printNode(xml,rootElem);
		xml.close();
	}
	
	private void printNode(XmlWriter xml, Node n) {
		xml.startElement(getTypeName(n.type));
		// add any attributes if they exist
		if(n.attrs != null) {
			for(int i = 0; i < n.attrs.length; i++) {
				if(n.attrs[i] != null) {
					xml.attribute(n.attrs[i].name, n.attrs[i].value);
				}
			}
		}
		// iterate through children
		if(n.children != null) {
			for(Node child : n.children){
				printNode(xml,child);
			}
		}
		else {
			// values don't have children, so could be value
			if(n.type == SV_VALUE) {
				xml.text(n.text);
			}
		}
		// finish element
		xml.endElement();
	}
	
	// removes the given node and then returns nextNode()
	private Node removeNode(Node rem) {
		// we should find the next node before array indexes are
		// changed by the node getting deleted
		Node next = nextNode(rem,true);
		
		Node folder = rem.parent;
		folder.children.remove(rem);
		return next;
	}
	
	// returns the next node in the in-order traversal of the tree
	private Node nextNode(Node n) {
		return nextNode(n,false);
	}
	// if dontGoDown is false it will not return a child of the given node
	private Node nextNode(Node n, boolean dontGoDown) {
		if(n.children != null && !dontGoDown) {
			return n.children.get(0);
		}
		if(n.parent != null) {
			// look upwards for a sibling that hasn't been visited
			Node last = n;
			Node p = n.parent;
			while(p != null) {
				int testIdx = p.children.indexOf(last)+1;
				if(testIdx >= p.children.size()) {
					// last was final child in array, so go up again
					last = p;
					p = last.parent;
				}
				else {
					return p.children.get(testIdx);
				}
			}
			// if we got to here without a return then we're out of nodes
		}
		return null;
	}
	
	private boolean isLegacyURL(Node n) {
		if(n.attrs == null)
			return false;
		for(int i = 0; i < n.attrs.length; i++) {
			if(n.attrs[i] != null && n.attrs[i].name.equals("sv:name") && 
					n.attrs[i].value.equals("legacyURL"))
				return true;
		}
		return false;
	}
	
	public int getType(String elemName) {
		if(elemName.equals("sv:node")) {
			return SV_NODE;
		}
		else if(elemName.equals("sv:property")) {
			return SV_PROPERTY;
		}
		else if(elemName.equals("sv:value")) {
			return SV_VALUE;
		}
		else {
			// uh oh
			System.out.println("Unrecognized node! " + elemName);
		}
		return 0;
	}
	
	public String getTypeName(int type) {
		switch(type) {
		case SV_NODE: 		return "sv:node";
		case SV_PROPERTY:	return "sv:property";
		case SV_VALUE: 		return "sv:value";
		default: System.out.println("Unknown node type! "+type);
		}
		return "UNKNOWN";
	}
	
	private int currentState = 0;

	// Event handling routines

	// At start of document, note we've begun
	public void startDocument()
		throws SAXException
	{
		// do nothing yay!
	}


	// At end of document, note we're done
	public void endDocument()
		throws SAXException
	{
		// do nothing
		System.out.println("Number of elements: "+elemNum);
	}

	// At the start of an element, indent, print that element as a label for the line
    public void startElement(String namespaceURI,	//namespace (we don't use)
                             String sName, // simple name (localName)
                             String qName, // qualified name
                             Attributes attrs)	//attribute list
    	throws SAXException
    {
    	elemNum++;
    	String elementName = sName;
    	if(elementName.equals(""))
    		elementName = qName;
    	
		currentState = 0;
    	
    	if(elementName.equals("sv:value")) {
    		currentState = IN_VALUE;
    	}
    	
    	if(rootElem == null) {
    		rootElem = new Node(null,getType(elementName));
    		curNode = rootElem;
    	}
    	else {
    		curNode = new Node(curNode,getType(elementName));
    	}
    	
    	for(int i = 0; i < attrs.getLength(); i++) {
    		curNode.addAttr(attrs.getValue(i), attrs.getType(i),attrs.getLocalName(i));
    	}
	}

	// At the end of an element, end the line, back off indentation
	public void endElement(String namespaceURI,	//namespace (we're not using)
	                         String sName, // simple name (localName)
	                         String qName) // qualified name
		throws SAXException
	{
		String elementName = sName;
    	if(elementName.equals(""))
    		elementName = qName;
    	
    	if(getType(elementName)!=curNode.type) {
    		System.out.println("I think something went wrong.");
    	}
    	curNode = curNode.parent;
    	currentState = 0;
	}

	// Write out the characters, if any, between the XML tags
	public void characters(char buf[], int offset, int len)
    	throws SAXException
    {
	    switch(currentState) {
	    case IN_VALUE: curNode.text=String.copyValueOf(buf, offset, len); break;
	    }
    }
}

